using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Security;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
#if MOSS
using Microsoft.SharePoint.Publishing;
#endif
using Microsoft.SharePoint.WebPartPages;
using PS.Practices.Provisioner;
using PS.Practices.Provisioner.Provisioner.Schema.Logic;
using PS.Practices.SiteBuilder.Features.API;
using PS.Practices.SiteBuilder.Features.Schema.DOM;
using PS.Practices.Util;
using System.Runtime.Serialization;

namespace PS.Practices.SiteBuilder.Features.Schema.DOM
{
    /// <summary>
    /// Object which represents the ProvisionerRoot xml node
    /// </summary>
    [XmlRoot("Provisioner")]
    public class ProvisionerRoot
    {
        public class MasterPageActions
        {
            public static string NoneAction = "{{None}}";
            public static string SetToParent = "{{Parent}}";
            public static string SetToRoot = "{{Root}}";
        }

        private List<WebOperation> webOperations;

        [XmlAttribute]
        public string ShowErrors { get; set; }

        [XmlElement(typeof(FixupLists))]
        [XmlElement(typeof(RestrictSiteTemplates))]
        [XmlElement(typeof(RestoreWebProperties))]
        [XmlElement(typeof(CustomizedCssFiles))]
        [XmlElement(typeof(SetStyling))]
        [XmlElement(typeof(ProvisionWebs))]
        [XmlElement(typeof(FixupWebACL))]
        [XmlElement(typeof(FixupFiles))]
        [XmlElement(typeof(ProvisionLists))]
        [XmlElement(typeof(WebFeatures))]
        public List<WebOperation> Operations
        {
            get { return webOperations; }
            set { webOperations = value; }
        }


        //! Applies specified web template to the provisioned web
        [XmlAttribute]
        public string ApplyWebTemplate { get; set; }

        private string setMasterPage = MasterPageActions.NoneAction;

        //! Sets MasterUrl
        /*! {{Root}}: Use the same master page as the root web */
        /*! {{Parent}}: Use the same master page as the parent web */
        [XmlAttribute]
        public string MasterUrl
        {
            get { return setMasterPage; }
            set { setMasterPage = value; }
        }

        private string setCustomMasterPage = MasterPageActions.NoneAction;

        //! Sets CustomMasterUrl
        /*! {{Root}}: Use the same master page as the root web */
        /*! {{Parent}}: Use the same master page as the parent web */
        [XmlAttribute]
        public string CustomMasterUrl
        {
            get { return setCustomMasterPage; }
            set { setCustomMasterPage = value; }
        }

        [XmlAttribute]
        public bool ReopenWebOnApplyWebTemplate { get; set; }

        public void Accept(IVisitorContext visitorContext)
        {
            try
            {
                var visitor = visitorContext.WebOperationVisitor;
                visitor.Visit(this);

                foreach (var webOp in webOperations)
                {
                    webOp.Accept(visitor);
                }
            }
            finally
            {
                visitorContext.Finalize();
            }
        }

        /// <summary>
        /// This method will only call the operations' accept methods, not its own.  Used webs which are provisioned via the ProvisionWebs element
        /// </summary>
        /// <param name="visitorContext"></param>
        /// <param name="operations"></param>
        public static void Accept(IVisitorContext visitorContext, IEnumerable<WebOperation> operations)
        {
            try
            {
                var visitor = visitorContext.WebOperationVisitor;

                foreach (var op in operations)
                {
                    op.Accept(visitor);
                }
            }
            finally
            {
                visitorContext.Finalize();
            }

        }
    }

    public class Extension
    {
        private string assembly;
        private string tagPrefix;

        [XmlAttribute()]
        public string Assembly
        {
            get { return assembly; }
            set { assembly = value; }
        }

        [XmlAttribute()]
        public string TagPrefix
        {
            get { return tagPrefix; }
            set { tagPrefix = value; }
        }


        public Extension(string assembly, string tagPrefix)
        {
            Assembly = assembly;
            TagPrefix = tagPrefix;
        }
    }

    public class WebFeatures : WebOperation
    {
        private List<string> _features;
        [XmlElement("Feature")]
        public List<string> Features
        {
            get
            {
                return _features;
            }
            set
            {
                _features = value;
            }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class Feature
    {
        [XmlAttribute("ID")]
        public string FeatureID
        { get; set; }
    }

    #region FixupWebACL web action

    /// <summary>
    /// Object which represents the FixupWebACL xml node
    /// </summary>
    public class FixupWebACL : WebOperation
    {
        private List<WebACLAction> webACLActions = new List<WebACLAction>();

        [XmlAttribute]
        public bool InheritTopNav { get; set; }

        [XmlAttribute]
        public bool RespectInheritanceSetting { get; set; }

        //! Determines the role assigment status of the provisioned web
        /*! Break*: Break role inheritance */
        /*! BreakAndCopy: Break role inheritance and copy permissions from parent */
        [XmlAttribute]
        public string RoleAssignments { get; set; }

        [XmlAttribute]
        public string SyncGroupQuickLaunch { get; set; }

        [XmlElement(typeof(AssociateSiteGroup))]
        [XmlElement(typeof(SiteGroup))]
        //[XmlElement(typeof(AddMembersToSiteGroup))]
        public List<WebACLAction> Operations
        {
            get { return webACLActions; }
            set { webACLActions = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);

            foreach (WebACLAction webACLAction in Operations)
            {
                webACLAction.Accept(visitor);
            }
        }
    }

    public abstract class WebACLAction
    {
        public abstract void Accept(IWebOperationVisitor visitor);
    }

    public class FbaRole : WebACLAction
    {
        [XmlAttribute]
        public string Action { get; set; }

        [XmlAttribute]
        public string Name { get; set; }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class AddMembersToSiteGroup : WebACLAction
    {
        //! Name of the group to add members
        [XmlAttribute]
        public string GroupName { get; set; }

        //! Members to add to the group.
        [XmlAttribute]
        public string Members { get; set; }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    /// <summary>
    /// Object which represents the SiteGroup node of the Provisioner XML
    /// </summary>
    public class SiteGroup : WebACLAction
    {
        //! Action to be taken on the site group
        /*! Create: Create a group with the specified settings */
        /*! Delete: Delete a group with the specified name*/
        [XmlAttribute]
        public string Action { get; set; }

        //! Name of the site group to have action taken
        [XmlAttribute]
        public string Name { get; set; }

        [XmlAttribute]
        public string Description { get; set; }

        [XmlAttribute()]
        public bool AllowMembersEditMembership { get; set; }

        [XmlAttribute()]
        public bool AllowRequestToJoinLeave { get; set; }

        [XmlAttribute()]
        public bool AutoAcceptRequestToJoinLeave { get; set; }

        [XmlAttribute()]
        public bool OnlyAllowMembersViewMembership { get; set; }

        [XmlAttribute()]
        public string RequestToJoinLeaveEmailSetting { get; set; }

        [XmlAttribute()]
        public string Owner { get; set; }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }


    public class User : WebACLAction
    {
        [XmlAttribute]
        public string Action { get; set; }

        [XmlAttribute]
        public string LoginName { get; set; }

        [XmlAttribute]
        public string DisplayName { get; set; }

        [XmlAttribute]
        public string Email { get; set; }

        [XmlAttribute]
        public string Description { get; set; }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class AssociateSiteGroup : WebACLAction
    {
        private string role;
        private string name;
        private string addToQuickLaunch;
        private SPWeb web;

        [XmlAttribute]
        public string Role
        {
            [DebuggerStepThrough]
            get { return role; }

            [DebuggerStepThrough]
            set { role = value; }
        }

        [XmlAttribute]
        public string Name
        {
            [DebuggerStepThrough]
            get { return name; }

            [DebuggerStepThrough]
            set { name = value; }
        }

        [XmlAttribute]
        public string AddToQuickLaunch
        {
            [DebuggerStepThrough]
            get { return addToQuickLaunch; }

            [DebuggerStepThrough]
            set { addToQuickLaunch = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }

        //public SPGroup GetGroup()
        //{
        //    name = ParserUtil.ReplaceLiveFields(name, web);
        //    return AclUtil.ResolveSiteGroup(name, web);
        //}
    }

    public class PrincipalRole : WebACLAction
    {
        private string role;
        private string name;
        private string type;
        private string action;

        [XmlAttribute]
        public string Role
        {
            [DebuggerStepThrough]
            get { return role; }

            [DebuggerStepThrough]
            set { role = value; }
        }

        [XmlAttribute]
        public string Name
        {
            [DebuggerStepThrough]
            get { return name; }

            [DebuggerStepThrough]
            set { name = value; }
        }

        [XmlAttribute]
        public string Type
        {
            [DebuggerStepThrough]
            get { return type; }

            [DebuggerStepThrough]
            set { type = value; }
        }

        [XmlAttribute]
        public string Action
        {
            [DebuggerStepThrough]
            get { return action; }

            [DebuggerStepThrough]
            set { action = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    #endregion

    #region RestrictSiteTemplates web action

    public class RestrictSiteTemplates : WebOperation
    {
        private List<Template> templates = new List<Template>();

        [XmlElement("Template")]
        public List<Template> Templates
        {
            get { return templates; }
            set { templates = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

#if MOSS
    public class RestrictPageLayouts : WebOperation
    {
        private List<Layout> layouts = new List<Layout>();

        [XmlElement("Layout")]
        public List<Layout> Layouts
        {
            get { return layouts; }
            set { layouts = value; }
        }

        public override void Execute(SPWeb web)
        {
            if (layouts.IsEmpty())
                WebUtil.RestrictPageLayouts(web, layouts.Select(l=> l.Value));
            web.Update();
        }
    }
#endif

    public class Template
    {
        private string lcid;
        private string value;

        [XmlAttribute]
        public string LCID
        {
            get { return lcid; }
            set { lcid = value; }
        }

        [XmlText]
        public string Value
        {
            get { return value; }
            set { this.value = value; }
        }
    }
    public class Layout
    {
        
        private string value;

        [XmlText]
        public string Value
        {
            get { return value; }
            set { this.value = value; }
        }
    }

    #endregion

    #region RestoreWebProperties web action

    public class RestoreWebProperties : WebOperation
    {
        private WebProperies webProperties;

        [XmlElement]
        public WebProperies WebProperties
        {
            get { return webProperties; }
            set { webProperties = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            if (this.WebProperties != null)
            {
                this.WebProperties.Accept(visitor);
            }
        }
    }

    public class WebProperies
    {
        private List<WebProperty> properties = new List<WebProperty>();

        [XmlElement("WebProperty")]
        public List<WebProperty> Properties
        {
            get { return properties; }
            set { properties = value; }
        }

        public void Execute(SPWeb web)
        {
            foreach (WebProperty webProperty in Properties)
            {
                if (!string.IsNullOrEmpty(webProperty.Key) && !string.IsNullOrEmpty(webProperty.Value))
                {
                    string key = webProperty.Key;
                    string value = webProperty.Value;
#if !SANDBOX
                    if (web.Properties.ContainsKey(key))
                    {
                        web.Properties[key] = value;
                    }
                    else
                    {
                        web.Properties.Add(key, value);
                    }
#else
                    if (web.AllProperties.ContainsKey(key))
                    {
                        Logger.Info("Updating Property: Key: " + key + ", Value: " + value);
                        web.AllProperties[key] = value;
                    }
                    else
                    {
                        Logger.Info("Adding Property: Key: " + key + ", Value: " + value);
                        web.AddProperty(key, value);
                    }
#endif
                }
            }

#if !SANDBOX
            web.Properties.Update();
#else
            web.AllowUnsafeUpdates = true;
            web.Update();
            web.AllowUnsafeUpdates = false;
#endif
        }

        public void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }


    public class WebProperty
    {
        private string key;
        private string value;

        [XmlAttribute]
        public string Key
        {
            get { return key; }
            set { key = value; }
        }

        [XmlAttribute]
        public string Value
        {
            get { return value; }
            set { this.value = value; }
        }
    }

    #endregion

    #region RestoreCss web action

    public class CustomizedCssFiles : WebOperation
    {
        private List<CustomizedCssFile> files = new List<CustomizedCssFile>();

        [XmlElement("CustomizedCssFile")]
        public List<CustomizedCssFile> Files
        {
            get { return files; }
            set { files = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }
    public class SetStyling : WebOperation
    {
        [XmlAttribute]
        public string ApplyTheme
        {
            get; set;
        }
        
        [XmlAttribute]
        public string AlternateCss
        {
            get; set;
        }

        [XmlAttribute]
        public string SiteLogoUrl
        {
            get;set;
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class CustomizedCssFile
    {
        private string name;

        [XmlAttribute]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }

    #endregion

    #region ProvisionLists web action

    /// <summary>
    /// Object which represents the ProvisionLists xml node
    /// </summary>
    public class ProvisionLists : WebOperation
    {
        private List<List> lists = new List<List>();


        //! Creates a list in the provisioned web
        [XmlElement("List")]
        public List<List> Lists
        {
            get { return lists; }
            set { lists = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }   


    #endregion ProvisionLists web action

    #region ProvisionWebs web action

    public class ProvisionWebs : WebOperation
    {
        private List<Web> webs = new List<Web>();

        [XmlElement("Web")]
        public List<Web> Webs
        {
            get { return webs; }
            set { webs = value; }
        }

        public override void Accept(IWebOperationVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    public class Web
    {
        public class ActionContainer
        {
            public ActionContainer()
            {
                Operations = new List<WebOperation>();
                Enabled = true;
            }

            [XmlElement(typeof(FixupLists))]
            [XmlElement(typeof(ProvisionLists))]
            [XmlElement(typeof(RestrictSiteTemplates))]
            [XmlElement(typeof(RestoreWebProperties))]
            [XmlElement(typeof(CustomizedCssFiles))]
            [XmlElement(typeof(SetStyling))]
            [XmlElement(typeof(ProvisionWebs))]
            [XmlElement(typeof(FixupWebACL))]
#if !SANDBOX
            [XmlElement(typeof(FixupFiles))]
#endif
            public List<WebOperation> Operations { get; set; }
            [XmlAttribute]
            public bool Enabled { get; set; }
        }

        public Web()
        {
            Webs = new List<Web>();
            Actions = new ActionContainer();
        }

        [XmlElement("Actions")]
        public ActionContainer Actions { get; set; }
        
        [XmlElement("Web")]
        public List<Web> Webs { get; set; }

        [XmlAttribute]
        public string Title { get; set; }

        [XmlAttribute]
        public string Description { get; set; }

        [XmlAttribute]
        public string Template { get; set; }

        [XmlAttribute]
        public string Url { get; set; }

        [XmlAttribute]
        public string LCID { get; set; }

        private bool? uniquePermissions;

        [XmlIgnore]
        public bool? UniquePermissions
        {
            get { return uniquePermissions; }
            set { uniquePermissions = value; }
        }

        private bool? inheritTopNav;

        [XmlIgnore]
        public bool? InheritTopNav
        {
            get { return inheritTopNav; }
            set { inheritTopNav = value; }
        }


        [XmlAttribute("UniquePermissions")]
        public string StrUniquePermissions
        {
            get { return GetValue(uniquePermissions); }
            set { SetBool(ref uniquePermissions, value); }
        }

        [XmlAttribute("InheritTopNav")]
        public string StrInheritTopNav
        {
            get { return GetValue(inheritTopNav); }
            set { SetBool(ref inheritTopNav, value); }
        }

        private static string GetValue(bool? value)
        {
            return value.HasValue ? value.Value.ToString() : null;
        }
        private static void SetBool(ref bool? prop, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                prop = bool.Parse(value);
            }
            else
                prop = null;
        }
    }
}

#endregion

